#include "vm_manager.h"
#include "svn_cmd.h"
#include "windowattrmanager.h"
#include <wx/msgdlg.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/file.h>

using namespace versionmgr;


SettingsDialogBase::SettingsDialogBase(wxWindow* parent, const Settings& settings, bool bUseExternalFile, IConfigTool* cfg)
: SettingsDialogGui(parent)
, m_settings(settings)
, m_cfg(cfg)
{
	m_fpSvnCommand->SetPath(svn::get_svn_cmd());

	// Store settings in GUI
	m_sFilename      = m_settings.m_sFilename;
	m_bAutoIncBuild  = false;
	m_sMajorVar      = m_settings.m_sMajor;
	m_sMinorVar      = m_settings.m_sMinor;
	m_sReleaseVar    = m_settings.m_sRelease;
	m_sBuildVar      = m_settings.m_sBuild;
	m_sVersionVar    = m_settings.m_sVersion;
	m_sStatusVar     = m_settings.m_sStatus;
	m_bUseNamespace  = m_settings.m_bUseNamespace;
	m_sNamespaceName = m_settings.m_sNamespace;
	m_bSvnBuild      = m_settings.m_bUseBuildSvnRev;

	m_bUseExternalSettingFile = bUseExternalFile;

	TransferDataToWindow();
}

bool SettingsDialogBase::SaveSettings()
{
	if (!TransferDataFromWindow()) {
		return false;
	}

	m_settings.m_sFilename       = m_sFilename;
	m_settings.m_sMajor          = m_sMajorVar;
	m_settings.m_sMinor          = m_sMinorVar;
	m_settings.m_sRelease        = m_sReleaseVar;
	m_settings.m_sBuild          = m_sBuildVar;
	m_settings.m_sStatus         = m_sStatusVar;
	m_settings.m_sVersion        = m_sVersionVar;
	m_settings.m_bUseNamespace   = m_bUseNamespace;
	m_settings.m_sNamespace      = m_sNamespaceName;
	m_settings.m_bUseBuildSvnRev = m_bSvnBuild;

	return true;
}

ProjectSettingsDialog::ProjectSettingsDialog(wxWindow* parent, const wxString& sProjectPath, const Settings& settings, bool bUseExternalFile, IConfigTool* cfg)
: SettingsDialogBase(parent, settings, bUseExternalFile, cfg)
, m_sProjectPath(sProjectPath)
{
	WindowAttrManager::Load(this, wxT("pmProjectSettingsDialog"), m_cfg);
	
	m_fpSvnCommand->Disable();

	// Make path absolute according to base path
	wxFileName fnVersionFile(m_sProjectPath + wxFileName::GetPathSeparator() + m_settings.m_sFilename);
	fnVersionFile.Normalize();
	m_dirVersionPath->SetPath(fnVersionFile.GetPath());

	TransferDataToWindow();
}

ProjectSettingsDialog::~ProjectSettingsDialog()
{
	WindowAttrManager::Save(this, wxT("pmProjectSettingsDialog"), m_cfg);
}

void ProjectSettingsDialog::OnOK( wxCommandEvent& event )
{
	if (!SaveSettings()) {
		return;
	}

	// Make version file path relative to base path
	wxFileName fnVersionFile(m_dirVersionPath->GetPath() + wxFileName::GetPathSeparator() + m_sFilename);
	fnVersionFile.MakeRelativeTo(m_sProjectPath);
	m_settings.m_sFilename = m_sFilename;

	EndDialog(wxID_OK);
}

GlobalSettingsDialog::GlobalSettingsDialog(wxWindow* parent, const Settings& settings, bool bUseExternalFile, IConfigTool* cfg)
: SettingsDialogBase(parent, settings, bUseExternalFile, cfg)
{
	WindowAttrManager::Load(this, wxT("pmGlobalSettingsDialog"), m_cfg);
	
	m_dirVersionPath->Disable();
}

GlobalSettingsDialog::~GlobalSettingsDialog()
{
	WindowAttrManager::Save(this, wxT("pmGlobalSettingsDialog"), m_cfg);
}

void GlobalSettingsDialog::OnOK( wxCommandEvent& event )
{
	if (!SaveSettings()) {
		return;
	}

	// Save SVN command
	svn::set_svn_cmd(m_fpSvnCommand->GetPath());

	EndDialog(wxID_OK);
}

VersionDialog::VersionDialog(wxWindow* parent, const VersionData& versionData, IConfigTool* cfg)
: VersionDialogGui(parent)
, m_version(versionData.m_version)
, m_cfg(cfg)
{
	WindowAttrManager::Load(this, wxT("pmVersionDialog"), m_cfg);
	
	m_major   = m_version.m_major;
	m_minor   = m_version.m_minor;
	m_release = m_version.m_release;
	m_build   = m_version.m_build;
	m_status  = m_version.m_status;
	if (versionData.m_settings.m_bUseBuildSvnRev) {
		m_editBuild->Disable();
		m_btnIncBuild->Disable();
	}
	TransferDataToWindow();
}

VersionDialog::~VersionDialog()
{
	WindowAttrManager::Save(this, wxT("pmVersionDialog"), m_cfg);
}

void VersionDialog::OnCheckCanEdit( wxCommandEvent& event )
{
	bool bEditable = event.IsChecked();
	m_editMajor->SetEditable(bEditable);
	m_editMinor->SetEditable(bEditable);
	m_editRelease->SetEditable(bEditable);
	m_editBuild->SetEditable(bEditable);
}

void VersionDialog::OnIncMajor( wxCommandEvent& event )
{
	TransferDataFromWindow();
	m_major++;
	m_minor   = 0;
	m_release = 0;
	m_build   = 0;
	m_status  = wxEmptyString;
	TransferDataToWindow();
}

void VersionDialog::OnIncMinor( wxCommandEvent& event )
{
	TransferDataFromWindow();
	m_minor++;
	m_release = 0;
	m_build   = 0;
	m_status  = wxEmptyString;
	TransferDataToWindow();
}

void VersionDialog::OnIncRelease( wxCommandEvent& event )
{
	TransferDataFromWindow();
	m_release++;
	m_build   = 0;
	m_status  = wxEmptyString;
	TransferDataToWindow();
}

void VersionDialog::OnIncBuild( wxCommandEvent& event )
{
	TransferDataFromWindow();
	m_build++;
	m_status  = wxEmptyString;
	TransferDataToWindow();
}

void VersionDialog::OnOK( wxCommandEvent& event )
{
	if (!TransferDataFromWindow()) {
		return;
	}

	m_version.m_major        = m_major;
	m_version.m_minor        = m_minor;
	m_version.m_release      = m_release;
	m_version.m_build        = m_build;
	m_version.m_status       = m_status;

	EndModal(wxID_OK);
}

/**
 * Make file comparison
 *
 * @param fnLeft  First file name
 * @param fnRight second file name
 *
 * @return true if file are identical
 */
bool wxFileCmp(const wxFileName& fnLeft, const wxFileName& fnRight)
{
	if (!fnLeft.FileExists() || !fnRight.FileExists()) {
		return false;
	}

	if (fnLeft.GetSize() != fnRight.GetSize()) {
		return false;
	}

	wxFile fLeft(fnLeft.GetFullPath());
	wxFile fRight(fnRight.GetFullPath());
	if (!fLeft.IsOpened() || !fRight.IsOpened()) {
		return false;
	}

	char bufLeft[256];
	char bufRight[256];
	size_t nLeft  = 0;
	size_t nRight = 0;
	do {
		nLeft  = fLeft.Read(bufLeft, sizeof bufLeft);
		nRight = fRight.Read(bufRight, sizeof bufRight);
		if (nLeft != nRight) {
			return false;
		}
		if (nLeft == wxInvalidOffset) {
			return false;
		}
		if (nLeft && memcmp(bufLeft, bufRight, nLeft) != 0) {
			return false;
		}
	}
	while (nLeft != 0);
	return true;
}

/**
 * Write the version file if the file has changed
 *
 * @param versionData all version information
 *
 * @return true on succes
 */
bool versionmgr::WriteVersionFile(const VersionData& versionData)
{
	static const wxChar wxEndl = wxT('\n');

	int major       = versionData.m_version.m_major;
	int minor       = versionData.m_version.m_minor;
	int release     = versionData.m_version.m_release;
	int build       = versionData.m_version.m_build;
	wxString status = versionData.m_version.m_status;

	wxString sMajorVar   = versionData.m_settings.m_sMajor;
	wxString sMinorVar   = versionData.m_settings.m_sMinor;
	wxString sReleaseVar = versionData.m_settings.m_sRelease;
	wxString sBuildVar   = versionData.m_settings.m_sBuild;
	wxString sStatusVar  = versionData.m_settings.m_sStatus;
	wxString sVersionVar = versionData.m_settings.m_sVersion;
	wxString sNamespace  = versionData.m_settings.m_sNamespace;

	if (versionData.m_settings.m_bUseBuildSvnRev) {
		long revision;
		if (svn::get_revision(revision)) {
			build = revision;
		}
	}

	wxString sStringVersion = wxString::Format(wxT("%d.%d.%d.%d %s"), major, minor, release, build, status.c_str());

	wxString sBuffer;
	sBuffer << wxT("#ifndef VERSIONMANAGER_VERSION_H_INCLUDED") << wxEndl;
	sBuffer << wxT("#define VERSIONMANAGER_VERSION_H_INCLUDED") << wxEndl;
	sBuffer << wxEndl;
	if (versionData.m_settings.m_bUseNamespace) {
		sBuffer << wxT("namespace ") << sNamespace << wxT(" {") << wxEndl;
		sBuffer << wxEndl;
	}
	sBuffer << wxT("static const int   ") << sMajorVar   << wxT(" = ")   << major   << wxT(";")   << wxEndl;
	sBuffer << wxT("static const int   ") << sMinorVar   << wxT(" = ")   << minor   << wxT(";")   << wxEndl;
	sBuffer << wxT("static const int   ") << sReleaseVar << wxT(" = ")   << release << wxT(";")   << wxEndl;
	sBuffer << wxT("static const int   ") << sBuildVar   << wxT(" = ")   << build   << wxT(";")   << wxEndl;
	sBuffer << wxT("static const char* ") << sStatusVar  << wxT(" = \"") << status  << wxT("\";") << wxEndl;
	sBuffer << wxEndl;
	sBuffer << wxT("static const char* ") << sVersionVar << wxT(" = \"") << sStringVersion << wxT("\";") << wxEndl;
	sBuffer << wxEndl;
	if (versionData.m_settings.m_bUseNamespace) {
		sBuffer << wxT("}; // namespace ") << sNamespace << wxEndl;
		sBuffer << wxEndl;
	}
	sBuffer << wxT("#endif // VERSIONMANAGER_VERSION_H_INCLUDED") << wxEndl;

	bool bSucces = false;
	if (::wxFileExists(versionData.m_settings.m_sFilename)) {
		// Create a temp file
		wxString sTmpFile = wxFileName::CreateTempFileName(wxT("vm"));
		wxFile fTmpFile(sTmpFile, wxFile::write);
		if (fTmpFile.IsOpened() && fTmpFile.Write(sBuffer)) {
			fTmpFile.Close();
			if (wxFileCmp(versionData.m_settings.m_sFilename, sTmpFile)) {
				// Files are identical : everything is fine
				bSucces = true;
			}
			else {
				// Copy the tmp file to the version file
				bSucces = ::wxCopyFile(sTmpFile, versionData.m_settings.m_sFilename);
			}
		}
		::wxRemoveFile(sTmpFile);
	}
	else {
		wxLogMessage(wxT("VersionManager : Creating file version %s"), sStringVersion.c_str());
		// The version file doesn't exists : create it
		wxFile versionFile(versionData.m_settings.m_sFilename, wxFile::write);
		bSucces = versionFile.IsOpened() && versionFile.Write(sBuffer);
	}
	return bSucces;
}
